package 狂神多线程;
/*
在你的死锁代码中，线程不会主动释放已持有的锁，
核心原因是 synchronized 锁的释放有严格的 “时机规则”—— 只有当线程退出同步块（或同步方法）时，才会自动释放锁。
 */

//导致程序卡死
public class DeadLock {
    public static void main(String[] args) {
        MakeUp g1 = new MakeUp(0, "灰姑凉");
        MakeUp g2 = new MakeUp(1, "白雪公主");
        g1.start();
        g2.start();
    }
}



class Lipstick{

}



class Mirror{

}



class MakeUp extends Thread{
    //需要的资源只有一份,用static来保证只有一份
    static Lipstick lipstick=new Lipstick();
    static Mirror mirror=new Mirror();
    int choice; //选择
    String girlName;

    public MakeUp(int choice, String girlName) {
        this.choice = choice;  //选择
        this.girlName = girlName; //使用化妆品的人
    }


    @Override
    public void run() {
         //化妆
         makeup();
    }

    //化妆,互相持有对方的锁,就是需要拿到对方的资源
    private void makeup() {
        if (choice == 0) {
            // 灰姑凉：先拿口红锁，且不释放，同时等待镜子锁
            synchronized (lipstick) {
                System.out.println(this.girlName + "获得口红的锁，等待镜子...");
                try {
                    Thread.sleep(1000); // 故意等待，让另一个线程有机会拿到镜子锁
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                // 嵌套同步块：持有口红锁的同时，尝试获取镜子锁
                synchronized (mirror) {
                    System.out.println(this.girlName + "获得镜子的锁，开始化妆");
                }
            }
        } else {
            // 白雪公主：先拿镜子锁，且不释放，同时等待口红锁
            synchronized (mirror) {
                System.out.println(this.girlName + "获得镜子的锁，等待口红...");
                try {
                    Thread.sleep(1000); // 故意等待，让另一个线程有机会拿到口红锁
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                // 嵌套同步块：持有镜子锁的同时，尝试获取口红锁
                synchronized (lipstick) {
                    System.out.println(this.girlName + "获得口红的锁，开始化妆");
                }
            }
        }
    }

}

/**
 * “资源唯一” 是死锁能发生的核心前提，我们可以结合代码和生活例子来理解：
 * 一、代码中 “资源唯一” 指的是什么？
 * 在你的代码里，“资源” 指的是 lipstick（口红）和 mirror（镜子）这两个对象。它们被 static 修饰，意味着：整个程序中，口红只有 1 支，镜子只有 1 面，所有线程（灰姑凉、白雪公主）抢的是 “同一支口红” 和 “同一面镜子”。
 * 二、为什么 “资源唯一” 很重要？
 * 死锁的本质是 “多个线程竞争同一批资源，且互相等待对方释放资源”。如果资源不唯一（比如每人各有一套），就不会有竞争，自然不会死锁。
 * 举个生活例子：
 * 场景 1（资源唯一）：只有 1 支口红和 1 面镜子。灰姑凉先拿起口红，白雪公主先拿起镜子。灰姑凉想拿白雪公主手里的镜子，白雪公主想拿灰姑凉手里的口红 —— 两人僵持（死锁）。
 * 场景 2（资源不唯一）：有 2 支口红和 2 面镜子（每人一套）。灰姑凉拿自己的口红和镜子，白雪公主拿自己的口红和镜子 —— 两人互不干扰，不会僵持。
 * 三、代码中如何保证 “资源唯一”？
 * 关键是 static 关键字：
 * java
 * 运行
 * class MakeUp extends Thread{
 *     // static修饰的变量，属于类本身，全程序只有1份
 *     static Lipstick lipstick=new Lipstick();
 *     static Mirror mirror=new Mirror();
 *     // ...
 * }
 * 无论创建多少个 MakeUp 实例（比如 g1、g2），它们访问的 lipstick 和 mirror 都是同一个对象。
 * 如果去掉 static，lipstick 和 mirror 会变成 “实例变量”（每个 MakeUp 实例各有一套），此时 g1 和 g2 抢的是自己的资源，不会死锁。
 * 总结
 * “资源唯一” 就是 “全局只有一份资源”，这是死锁的前提 —— 只有大家抢的是同一批东西，才可能出现 “你拿我的，我拿你的，互相不放” 的僵持局面。如果资源能随便复制（不唯一），就不存在竞争，自然不会死锁～
 */


/**
 * 或者说只创建一个  new Lipstick() 但没有用static修饰他不也是唯一的吗
 * 你的这个疑问很关键！核心在于：“只 new 一个 Lipstick” 是否能保证 “资源唯一”，
 * 取决于这个对象的 “创建位置” 和 “访问方式”—— 如果没加 static，即使只写了一次new Lipstick()
 * ，也可能因为 “类实例化” 导致出现多个对象，最终资源不唯一。
 * 先看第一个场景：没加 static，把new Lipstick()写在 “类的成员变量” 位置
 * 比如你的原代码如果去掉 static：
 * java
 * 运行
 * class MakeUp extends Thread{
 *     // 没加static：这是“实例成员变量”
 *     Lipstick lipstick = new Lipstick(); // 看起来只写了一次new，但实际会被多次执行
 *     static Mirror mirror = new Mirror();
 *     // ... 其他代码
 * }
 * 这种情况下，资源不唯一，原因是：
 * 当你创建MakeUp的实例（比如g1 = new MakeUp(...)、g2 = new MakeUp(...)）时，
 * 每个实例都会单独执行一次Lipstick lipstick = new Lipstick()。
 * 最终结果：g1有一个自己的lipstick对象，g2也有一个自己的lipstick对象 —— 相当于有 2 支口红，
 * 资源不唯一，线程不会竞争，自然也不会死锁。
 * 再看第二个场景：没加 static，但在 “外部手动 new 一次，再传给所有线程”
 * 比如在main方法里只 new 一次Lipstick，然后通过构造方法传给所有MakeUp实例：
 * java
 * 运行
 * public class DeadLock {
 *     public static void main(String[] args) {
 *         // 关键：只在main里new一次Lipstick，全局只有这一个对象
 *         Lipstick onlyOneLipstick = new Lipstick();
 *
 *         // 把同一个Lipstick对象传给g1和g2
 *         MakeUp g1 = new MakeUp(0, "灰姑凉", onlyOneLipstick);
 *         MakeUp g2 = new MakeUp(1, "白雪公主", onlyOneLipstick);
 *         g1.start();
 *         g2.start();
 *     }
 * }
 *
 * class MakeUp extends Thread{
 *     Lipstick lipstick; // 没加static，只是一个引用
 *     static Mirror mirror = new Mirror();
 *     // ... 构造方法接收外部传进来的Lipstick对象
 *     public MakeUp(int choice, String girlName, Lipstick lipstick) {
 *         this.choice = choice;
 *         this.girlName = girlName;
 *         this.lipstick = lipstick; // 让g1和g2的lipstick指向同一个对象
 *     }
 *     // ... 其他代码
 * }
 * 这种情况下，资源是唯一的，原因是：
 * g1和g2的lipstick变量，指向的是main方法里 “唯一 new 的那一个onlyOneLipstick对象”。
 * 本质是通过 “手动传递对象引用”，让所有线程共享同一个资源 —— 这时候虽然没加 static，但资源依然唯一，线程会竞争，可能死锁。
 * 总结：static 的核心作用是 “自动保证全局唯一且共享”
 * 你说的 “只 new 一个 Lipstick 但不用 static”，只有在 “手动让所有线程共享这一个对象”（比如场景 2）时
 * ，资源才唯一；如果像场景 1 那样，把new Lipstick()写在 “非 static 的成员变量” 位置，会因为 “类实例化多次” 导致多个对象，资源不唯一。
 * 而加 static 的好处是：不用手动传递，JVM 会自动保证这个对象 “全局只初始化一次”，所有类实例都共享它—— 相当于帮你自动实现了 “资源唯一”，
 * 不用自己操心传递引用的问题。
 * 那场景一用了static就唯一了吗
 * 是的，场景一中如果用 static 修饰 Lipstick 对象，资源就会全局唯一。
 * 具体分析场景一：
 * 场景一的代码结构是：在 MakeUp 类中，将 Lipstick 声明为 static 成员变量：
 * java
 * 运行
 * class MakeUp extends Thread{
 *     // 用static修饰：属于MakeUp类本身
 *     static Lipstick lipstick = new Lipstick();
 *     // ... 其他代码
 * }
 * 这种情况下，lipstick 满足 “资源唯一” 的核心原因是：static 成员变量的初始化时机是类加载时（整个程序运行期间，MakeUp 类只会被加载一次），
 * 且初始化后全局只有这一个实例，所有 MakeUp 的对象（比如 g1、g2）访问的 lipstick 都是同一个对象。
 * 举个例子：
 * 当你创建两个 MakeUp 实例时：
 * java
 * 运行
 * MakeUp g1 = new MakeUp(0, "灰姑凉");
 * MakeUp g2 = new MakeUp(1, "白雪公主");
 * g1.lipstick 和 g2.lipstick 指向的是同一个 Lipstick 对象（因为 static 变量属于类，而非实例）。
 * 无论创建多少个 MakeUp 实例，static Lipstick lipstick 都只会被初始化一次，全局唯一。
 * 关键结论：
 * 场景一中，static 修饰的 Lipstick 对象一定是唯一的—— 这是 static 关键字的特性决定的（类级共享、仅初始化一次）。正因为如此，
 * 两个线程（g1 和 g2）才会竞争同一支口红，为死锁的发生提供了 “资源唯一” 的前提。
 */